package ace.module.oauth2.server.sdk.core.service.impl;

import ace.cmp.core.model.R;
import ace.cmp.json.api.JsonService;
import ace.module.oauth2.server.api.enums.Oauth2GrantTypeEnum;
import ace.module.oauth2.server.api.model.dto.ext.Oauth2AccessTokenDto;
import ace.module.oauth2.server.api.model.input.Oauth2GetTokenByExternalUserInput;
import ace.module.oauth2.server.sdk.core.feign.impl.Oauth2AuthFeign;
import ace.module.oauth2.server.sdk.core.feign.model.dto.Oauth2AccessTokenStandardDto;
import ace.module.oauth2.server.sdk.core.feign.model.dto.Oauth2ErrorResultDto;
import ace.module.oauth2.server.sdk.core.feign.model.request.Oauth2ExternalUserRequest;
import ace.module.oauth2.server.sdk.core.service.Oauth2AuthApiService;
import ace.module.oauth2.server.sdk.core.util.AuthorizationUtils;
import feign.Response;
import jakarta.annotation.Nullable;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.SneakyThrows;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

/**
 * @author caspar
 * @date 2023/3/3 13:34 辅助调用 {@link Oauth2AuthFeign } api
 */
@Component
public class Oauth2AuthApiServiceImpl implements Oauth2AuthApiService {

  private final Oauth2AuthFeign oauth2AuthFeign;
  private final JsonService jsonService;

  public Oauth2AuthApiServiceImpl(Oauth2AuthFeign oauth2AuthFeign, JsonService jsonService) {
    this.oauth2AuthFeign = oauth2AuthFeign;
    this.jsonService = jsonService;
  }

  @Override
  public R<Oauth2AccessTokenDto> getTokenByExternalUserGrantType(@Valid @NotNull Oauth2GetTokenByExternalUserInput input) {

    String authorization = AuthorizationUtils.getBasicAuthorization(input.getProxyClientId(), input.getProxyClientSecret());

    Oauth2ExternalUserRequest request = Oauth2ExternalUserRequest.builder()
        .accountId(input.getAccountId())
        .clientId(input.getClientId())
        .clientSecret(input.getClientSecret())
        .attributes(input.getAttributes())
        .build();

    Response response = oauth2AuthFeign.getTokenByGrantType(authorization, Oauth2GrantTypeEnum.EXTERNAL_USER_GRANT_TYPE.getCode(), request);

    R<Oauth2AccessTokenStandardDto> resultOut = this.resolveFromResponse(response);

    R<Oauth2AccessTokenDto> result = this.convert(resultOut);

    return result;
  }

  @Override
  public R<Oauth2AccessTokenDto> getTokenByRestGrantTypeAndPasswordMethod(String authorization, String username, String password, @Nullable Long captchaItemId, @Nullable String code) {
//TODO 实现修改过期的rest方法
    throw new RuntimeException("not implement");
//    Oauth2AceAuthenticationInput request = Oauth2AceAuthenticationInput.builder()
//        .authType(Oauth2RegisteredClientAuthConfigAuthenticationTypeEnum.USERNAME.getCode())
//        .bizParams(
//            Oauth2AceAuthParamsUsername.builder()
//                .username(username)
//                .password(password)
//                .captchaItemId(captchaItemId)
//                .code(code)
//                .build()
//        )
//
//        .build();
//
//    Response response = oauth2AuthFeign.getTokenByGrantType(authorization, Oauth2GrantTypeEnum.REST_GRANT_TYPE.getCode(), request);
//
//    R<Oauth2AccessTokenStandardDto> resultOut = this.resolveFromResponse(response);
//
//    R<Oauth2AccessTokenDto> result = this.convert(resultOut);
//
//    return result;
  }

  @SneakyThrows
  private R<Oauth2AccessTokenStandardDto> resolveFromResponse(Response response) {

    if (response.body() != null) {
      String responseBody =
          new BufferedReader(new InputStreamReader(response.body().asInputStream()))
              .lines()
              .collect(Collectors.joining());

      if (response.status() == HttpStatus.OK.value()) {

        Oauth2AccessTokenStandardDto accessTokenDTO =
            jsonService.toObject(responseBody, Oauth2AccessTokenStandardDto.class);

        return R.ok(accessTokenDTO);
      } else {
        Oauth2ErrorResultDto errorResultDTO;
        try {
          errorResultDTO = jsonService.toObject(responseBody, Oauth2ErrorResultDto.class);
        } catch (Exception ex) {
          throw new RuntimeException(responseBody);
        }
        R<Oauth2AccessTokenStandardDto> result =
            R.<Oauth2AccessTokenStandardDto>builder()
                .data(null)
                .message(
                    String.format(
                        "error: %s,error description: %s",
                        errorResultDTO.getError(), errorResultDTO.getErrorDescription()))
                .code(response.status())
                .build();

        return result;
      }
    } else {
      R<Oauth2AccessTokenStandardDto> result =
          R.<Oauth2AccessTokenStandardDto>builder()
              .data(null)
              .message(response.reason())
              .code(response.status())
              .build();

      return result;
    }
  }

  private R<Oauth2AccessTokenDto> convert(R<Oauth2AccessTokenStandardDto> outDTOR) {
    R<Oauth2AccessTokenDto> result = new R<>();
    result.setMessage(outDTOR.getMessage());
    result.setCode(outDTOR.getCode());

    Oauth2AccessTokenDto data =
        Optional.ofNullable(outDTOR.getData())
            .map(
                outDTO -> {
                  Oauth2AccessTokenDto dto =
                      Oauth2AccessTokenDto.builder()
                          .accessToken(outDTO.getAccessToken())
                          .accessType(outDTO.getAccessType())
                          .attributes(outDTO.getAttributes())
                          .expiresIn(outDTO.getExpiresIn())
                          .idToken(outDTO.getIdToken())
                          .refreshToken(outDTO.getRefreshToken())
                          .scopes(outDTO.getScopes())
                          .build();
                  return dto;
                })
            .orElse(null);
    result.setData(data);
    return result;
  }
}
